package games;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

/**
 * 简单俄罗斯方块:一个精简的俄罗斯方块游戏
 *
 * @author 何老师
 */
public class SimpleTetrisGame extends JFrame implements ActionListener, KeyListener {
    public static final Color[] COLORS = {Color.ORANGE, Color.BLUE, Color.CYAN, Color.YELLOW, Color.GREEN, Color.RED, Color.MAGENTA};
    private static final int[][][] SHAPES = {
            {{1, 0}, {1, 0}, {1, 1}}, // L
            {{0, 1}, {0, 1}, {1, 1}}, // J
            {{1}, {1}, {1}, {1}}, // I
            {{1, 1}, {1, 1}}, // O
            {{0, 1, 1}, {1, 1, 0}}, // S
            {{1, 1, 0}, {0, 1, 1}}, // Z
            {{1, 1, 1}, {0, 1, 0}}  // T
    };

    private static final int GRID_WIDTH = 10;       // 游戏下落区域的宽度（方块数量）
    private static final int GRID_HEIGHT = 20;      // 游戏下落区域的深度（方块数量）
    private static final int CELL_SIZE = 30;        // 每个方块的大小（屏幕的像素点）
    private static final int TIMER_DELAY = 800;     // 每一次下落的延时时间（毫秒）
    private final boolean[][] grid;                 // 下落区域的数组，用于保存下落区域的状态
    private final Timer timer;                      // 定时器，启动后，每隔 TIMER_DELAY 毫秒执行一次 actionPerformed 方法
    private boolean isRunning = true;               // 是否在运行的标志位
    private int currentX, currentY;                 // 当前正在移动的方块的坐标
    private Tetromino currentTetromino;             // 当前正在移动的方块
    private int score;                              // 积分

    public SimpleTetrisGame() {                     // 构造函数，初始化游戏的一些参数
        setTitle("整活儿俄罗斯方块");
        setSize(GRID_WIDTH * CELL_SIZE, GRID_HEIGHT * CELL_SIZE); // 基于GRID_WIDTH和GRID_HEIGHT计算JFrame大小
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);                     // 设置默认行为，当关闭窗口的时候，退出程序。
        setLocationRelativeTo(null);                                        // 设置窗口位置不与任何其他对象相关，即屏幕居中

        newTetromino();                                 // 新建第一个方块
        score = 0;                                      // 初始积分为 0
        grid = new boolean[GRID_HEIGHT][GRID_WIDTH];    // 基于GRID_HEIGHT和GRID_WIDTH初始化grid
        timer = new Timer(TIMER_DELAY, this);     // 初始化定时器对象，每次时间为 TIMER_DELAY，响应对象为 自己（this）
        timer.start();                                  // 启动定时器
        addKeyListener(this);                         // 设置按键响应对象，自己（this）
        setFocusable(true);                             // 获得输入焦点
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                SimpleTetrisGame game = new SimpleTetrisGame();
                game.setVisible(true);
            }
        });
    }

    private void newTetromino() {
        currentTetromino = new Tetromino();
        currentX = GRID_WIDTH / 2; // 初始位置在 grid 中的坐
        currentY = 0;
    }

    private void drawCell(Graphics g, int x, int y, Color color) {
        // 绘制主体颜色
        g.setColor(color);
        g.fillRect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE);
        // 绘制阴影效果
        g.setColor(color.darker());
        g.fillRect(x * CELL_SIZE, y * CELL_SIZE + CELL_SIZE / 2, CELL_SIZE, CELL_SIZE / 2); // 右侧阴影
    }

    private void drawTetromino(Graphics g, int x, int y, Tetromino t) {
        int[][] shape = t.getShape();
        for (int i = 0; i < shape.length; i++) {
            for (int j = 0; j < shape[i].length; j++) {
                if (shape[i][j] == 1) {
                    drawCell(g, (x + j), (y + i), t.getColor()); // 将 grid 坐标转换为 JFrame 坐标
                }
            }
        }
    }

    private void drawBackgroundText(Graphics g2d) {
        String text0 = "来", text1 = "何老师", text2 = "的Java课", text3 = "整活儿";
        Font font = new Font("微软雅黑", Font.BOLD, 64);
        g2d.setFont(font);
        g2d.setColor(new Color(192, 192, 192)); // 设置透明度为30%
        FontMetrics fm = g2d.getFontMetrics();
        int x0 = (getWidth() - fm.stringWidth(text0)) / 2;
        int x1 = (getWidth() - fm.stringWidth(text1)) / 2;
        int x2 = (getWidth() - fm.stringWidth(text2)) / 2;
        int x3 = (getWidth() - fm.stringWidth(text3)) / 2;
        int y0 = 100, y1 = 200, y2 = 300, y3 = 400;
        g2d.drawString(text0, x0, y0);
        g2d.drawString(text1, x1, y1);
        g2d.drawString(text2, x2, y2);
        g2d.drawString(text3, x3, y3);
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g); // 执行父类的paint方法
        drawBackgroundText(g); // 绘制下落中的方块，用彩色。
        drawTetromino(g, currentX, currentY, currentTetromino);
        // 绘制已经落下的方块，用灰色
        for (int i = 0; i < grid.length; i++) {
            for (int j = 0; j < grid[i].length; j++) {
                if (grid[i][j]) {
                    drawCell(g, j, i, Color.GRAY);
                }
            }
        }
        // 添加网格线绘制逻辑，看起来好看点儿
        g.setColor(new Color(224, 222, 222));
        for (int i = 1; i < GRID_HEIGHT; i++) {
            g.drawLine(0, i * CELL_SIZE, GRID_WIDTH * CELL_SIZE, i * CELL_SIZE);
        }
        for (int j = 1; j < GRID_WIDTH; j++) {
            g.drawLine(j * CELL_SIZE, 0, j * CELL_SIZE, GRID_HEIGHT * CELL_SIZE);
        }
    }


    /**
     * 这是重写了 ActionListener 这个 interface 的方法，这个方法会在定时器 timer 到期的时候会自动被调用。
     * 处理动作事件的回调函数。
     * 当与组件关联的动作事件发生时，系统会自动调用此方法。
     * 此方法的具体实现是调用 `moveDown()` 函数，执行向下移动的操作。
     *
     * @param e 触发此回调的动作事件对象，包含事件的相关信息。
     */
    @Override
    public void actionPerformed(ActionEvent e) {
        moveDown();
    }

    private void moveDown() {
        if (!checkCollision(currentX, currentY + 1)) { // 检查 grid 坐标
            currentY++;
        } else {
            placeTetromino();
            clearCompletedLines();
            newTetromino();
            if (checkCollision(currentX, currentY)) {
                gameOver();
            }
        }
        repaint();
    }

    private boolean checkCollision(int x, int y) {
        return checkCollisionWithShape(x, y, currentTetromino.getShape());
    }

    private boolean checkCollisionWithShape(int x, int y, int[][] shape) {
        for (int i = 0; i < shape.length; i++) {
            for (int j = 0; j < shape[i].length; j++) {
                if (shape[i][j] == 1) {
                    int row = y + i;
                    int col = x + j;
                    // 添加边界条件检查
                    if (row < 0 || row >= grid.length || col < 0 || col >= grid[0].length || grid[row][col]) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    private void placeTetromino() {
        int[][] shape = currentTetromino.getShape();
        for (int i = 0; i < shape.length; i++) {
            for (int j = 0; j < shape[i].length; j++) {
                if (shape[i][j] == 1) {
                    grid[currentY + i][currentX + j] = true;
                }
            }
        }
    }

    private void clearCompletedLines() {
        for (int i = 0; i < grid.length; i++) {
            boolean fullLine = true;
            for (boolean cell : grid[i]) {
                if (!cell) {
                    fullLine = false;
                    break;
                }
            }
            if (fullLine) {
                score += 100;
                for (int k = i; k > 0; k--) {
                    grid[k] = grid[k - 1].clone();
                }
                grid[0] = new boolean[grid[0].length];
            }
        }
    }

    private void gameOver() {
        timer.stop();
        JOptionPane.showMessageDialog(this, "Game Over! Your score: " + score);
    }

    @Override
    public void keyPressed(KeyEvent e) {
        int key = e.getKeyCode();
        if (key == KeyEvent.VK_LEFT && !checkCollision(currentX - 1, currentY)) { // 检查 grid 坐标
            currentX -= 1;
        } else if (key == KeyEvent.VK_RIGHT && !checkCollision(currentX + 1, currentY)) { // 检查 grid 坐标
            currentX += 1;
        } else if (key == KeyEvent.VK_UP) {
            if (!checkCollisionWithShape(currentX, currentY, currentTetromino.getRotateShape())) {
                currentTetromino.rotate();
            }
        } else if (key == KeyEvent.VK_DOWN) {
            moveDown();
        } else if (key == KeyEvent.VK_SPACE) {
            dropToBottom();
        } else if (key == KeyEvent.VK_P) {
            if (isRunning) {
                timer.stop();
            } else {
                timer.start();
            }
            isRunning = !isRunning;
        }
        repaint();
    }

    @Override
    public void keyReleased(KeyEvent e) {
    }

    @Override
    public void keyTyped(KeyEvent e) {
    }

    private void dropToBottom() {
        while (!checkCollision(currentX, currentY + 1)) { // 检查 grid 坐标
            currentY++;
        }
        placeTetromino();
        clearCompletedLines();
        newTetromino();
        if (checkCollision(currentX, currentY)) {
            gameOver();
        }
    }

    /**
     * 方块的类，存储方块的颜色和形状，并且完成方块数组的旋转运算
     */
    private class Tetromino {
        private final Color color;
        private int[][] shape;

        public Tetromino() {
            int shapeIndex = (int) (Math.random() * SHAPES.length);
            this.shape = SHAPES[shapeIndex];
            this.color = COLORS[shapeIndex]; // 根据索引设置颜色
        }

        public int[][] getShape() {
            return shape;
        }

        public Color getColor() {
            return this.color;
        }

        public int[][] getRotateShape() {
            int rows = shape.length;
            int cols = shape[0].length;
            int[][] newShape = new int[cols][rows];
            for (int i = 0; i < rows; i++) {
                for (int j = 0; j < cols; j++) {
                    newShape[j][rows - 1 - i] = shape[i][j];
                }
            }
            return newShape;
        }

        public void rotate() {
            shape = getRotateShape();
        }
    }
}